////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxchinodewrapper.cpp
/// @brief Chi node wrapper class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <cutils/properties.h>
#include "camxchi.h"
#include "camxchinodewrapper.h"
#include "camxsettingsmanager.h"
#include "camxhal3metadatautil.h"
#include "camxhwenvironment.h"
#include "camxpipeline.h"
#include "inttypes.h"
#include "camximagebuffer.h"
#include "camxthreadmanager.h"
#include "camxtuningdatamanager.h"
#include "chincsdefs.h"
#include "camxtitan17xdefs.h"

// NOWHINE FILE CP036a: const_cast<> is prohibited

CAMX_NAMESPACE_BEGIN

static const INT    FenceCompleteProcessSequenceId   = -2;

static const UINT32 MaximumChiNodeWrapperInputPorts  = 32; ///< The maximum input ports for Chi node wrapper
static const UINT32 MaximumChiNodeWrapperOutputPorts = 32; ///< The maximum output ports for Chi node wrapper
static const CHAR*  pMemcpyNodeName = "com.qti.node.memcpy";

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ChiNodeWrapper* ChiNodeWrapper::Create(
    const NodeCreateInputData*   pCreateInputData,
    NodeCreateOutputData*        pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateOutputData);
    CHAR   prop[PROPERTY_VALUE_MAX];
    UINT32 enableDummynode = 0;

    property_get("persist.vendor.camera.enabledummynode", prop, "0");
    enableDummynode = atoi(prop);

    CamxResult      result       = CamxResultSuccess;
    ChiNodeWrapper* pChiNode     = CAMX_NEW ChiNodeWrapper();
    if (NULL != pChiNode)
    {
        ExternalComponentInfo externalComponentInfo = {0};

        if (1 == enableDummynode)
        {
            // NOWHINE CP036a: Const cast allowed for conversion to interfaces
            externalComponentInfo.pComponentName = const_cast<CHAR*>(pMemcpyNodeName);
        }
        else
        {
            externalComponentInfo.pComponentName = static_cast<CHAR*>(pCreateInputData->pNodeInfo->pNodeProperties->pValue);
        }

        result = HwEnvironment::GetInstance()->SearchExternalComponent(&externalComponentInfo, 1);
        if (CamxResultSuccess == result)
        {
            memcpy(&pChiNode->m_nodeCallbacks, &externalComponentInfo.nodeCallbacks, sizeof(ChiNodeCallbacks));

            pChiNode->m_numInputPort  = pCreateInputData->pNodeInfo->inputPorts.numPorts;
            pChiNode->m_numOutputPort = pCreateInputData->pNodeInfo->outputPorts.numPorts;

            CAMX_ASSERT(0 < pChiNode->m_numInputPort);
            CAMX_ASSERT(0 < pChiNode->m_numOutputPort);

            pChiNode->m_phInputBuffer   = pChiNode->AllocateChiBufferHandlePool(
                                            pChiNode->m_numInputPort * MaxRequestQueueDepth);
            pChiNode->m_phOutputBuffer  = pChiNode->AllocateChiBufferHandlePool(pChiNode->m_numOutputPort * MaxOutputBuffers);

            UINT32 size = (sizeof(CHINODEBYPASSINFO) * pChiNode->m_numOutputPort);
            pChiNode->m_pBypassData = static_cast<CHINODEBYPASSINFO*>(CAMX_CALLOC(size));
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupChi, "Failed to load Chi interface for %s", externalComponentInfo.pComponentName);
        }

        pChiNode->m_pChiContext = pCreateInputData->pChiContext;

        if ((NULL == pChiNode->m_phInputBuffer) || (NULL == pChiNode->m_phOutputBuffer))
        {
            CAMX_LOG_ERROR(CamxLogGroupChi, "Failed to allocate memory");
            CAMX_DELETE pChiNode;
            pChiNode = NULL;
        }

    }

    return pChiNode;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNCacheOps
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNCacheOps(
    CHINODEBUFFERHANDLE hChiBuffer,
    BOOL                invalidata,
    BOOL                clean)
{
    CDKResult     result        = CDKResultEFailed;
    ChiImageList* pImageBuffers = static_cast<ChiImageList*>(hChiBuffer);

    for (UINT i = 0; i < pImageBuffers->numberOfPlanes; i++)
    {
        if (0 != pImageBuffers->handles[i])
        {
            CSLMemHandle hCSLHandle = static_cast<CSLMemHandle>(pImageBuffers->handles[i]);

            result = CSLBufferCacheOp(hCSLHandle, invalidata, clean);
            if ( result !=  CDKResultSuccess)
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "Buffer cache op failed %d", result);
                break;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNGetMetadata(
    CHIMETADATAINFO* pMetadataInfo)
{
    CAMX_ASSERT(NULL != pMetadataInfo);
    CAMX_ASSERT(NULL != pMetadataInfo->chiSession);

    CDKResult result = CDKResultSuccess;
    if (pMetadataInfo->size <= sizeof(CHIMETADATAINFO))
    {
        ChiNodeWrapper* pNode   = static_cast<ChiNodeWrapper*>(pMetadataInfo->chiSession);
        result                  = pNode->GetMetadata(pMetadataInfo);
    }
    else
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "MedatadataInfo size mismatch");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNSetMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNSetMetadata(
    CHIMETADATAINFO* pMetadataInfo)
{
    CAMX_ASSERT(NULL != pMetadataInfo);
    CAMX_ASSERT(NULL != pMetadataInfo->chiSession);

    CDKResult result = CDKResultSuccess;
    if (pMetadataInfo->size <= sizeof(CHIMETADATAINFO))
    {
        ChiNodeWrapper* pNode   = static_cast<ChiNodeWrapper*>(pMetadataInfo->chiSession);
        result                  = pNode->SetMetadata(pMetadataInfo);
    }
    else
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "MedatadataInfo size mismatch");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetTuningmanager
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* ChiNodeWrapper::FNGetTuningmanager(
    CHIHANDLE hChiSession)
{
    CAMX_ASSERT(NULL != hChiSession);

    TuningDataManager* pTuningManager     = NULL;
    TuningSetManager*  pTuningDataManager = NULL;

    ChiNodeWrapper* pNode = static_cast<ChiNodeWrapper*>(hChiSession);
    if (NULL != pNode)
    {
        pTuningManager = pNode->GetTuningDataManager();
        if ((NULL != pTuningManager) &&
            (TRUE == pTuningManager->IsValidChromatix()))
        {
            pTuningDataManager = static_cast<TuningSetManager*>(pTuningManager->GetChromatix());
        }
    }
    return pTuningDataManager;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetVendorTagBase
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNGetVendorTagBase(
    CHIVENDORTAGBASEINFO* pVendorTagBaseInfo)
{
    CAMX_ASSERT(NULL != pVendorTagBaseInfo);
    CDKResult result = CDKResultSuccess;
    if (pVendorTagBaseInfo->size <= sizeof(CHIVENDORTAGBASEINFO))
    {
        UINT32 sectionBase      = 0;
        CamxResult resultCamx   = CamxResultSuccess;
        if (NULL == pVendorTagBaseInfo->pTagName)
        {
            resultCamx = VendorTagManager::QueryVendorTagSectionBase(pVendorTagBaseInfo->pComponentName, &sectionBase);
        }
        else
        {
            resultCamx = VendorTagManager::QueryVendorTagLocation(pVendorTagBaseInfo->pComponentName,
                                                                  pVendorTagBaseInfo->pTagName, &sectionBase);
        }

        if (CamxResultSuccess != resultCamx)
        {
            result = CDKResultEFailed;
        }
        else
        {
            pVendorTagBaseInfo->vendorTagBase = sectionBase;
        }
    }
    else
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "CHIVENDORTAGBASEINFO size mismatch");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNProcRequestDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNProcRequestDone(
    CHINODEPROCESSREQUESTDONEINFO* pInfo)
{
    CAMX_ASSERT(NULL != pInfo);
    CAMX_ASSERT(NULL != pInfo->hChiSession);

    CDKResult result = CDKResultSuccess;
    if (pInfo->size <= sizeof(CHINODEPROCESSREQUESTDONEINFO))
    {
        ChiNodeWrapper* pNode   = static_cast<ChiNodeWrapper*>(pInfo->hChiSession);
        result                  = pNode->ProcRequestDone(pInfo);
    }
    else
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "RequestDone Info size mismatch");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNProcMetadataDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNProcMetadataDone(
    CHINODEPROCESSMETADATADONEINFO* pInfo)
{
    CAMX_ASSERT(NULL != pInfo);
    CAMX_ASSERT(NULL != pInfo->hChiSession);

    CDKResult result = CDKResultSuccess;
    if (pInfo->size <= sizeof(CHINODEPROCESSMETADATADONEINFO))
    {
        ChiNodeWrapper* pNode = static_cast<ChiNodeWrapper*>(pInfo->hChiSession);
        result                = pNode->ProcMetadataDone(pInfo);
    }
    else
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "MetadataDone Info size mismatch");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNCreateFence
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNCreateFence(
    CHIHANDLE               hChiSession,
    CHIFENCECREATEPARAMS*   pInfo,
    CHIFENCEHANDLE*         phChiFence)
{
    CAMX_ASSERT(NULL != pInfo);
    CAMX_ASSERT(NULL != hChiSession);

    CDKResult result = CDKResultSuccess;
    if (NULL == phChiFence)
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "phChiFence is NULL");
        result = CDKResultEInvalidArg;
    }
    else if (pInfo->size > sizeof(CHIFENCECREATEPARAMS))
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "CreateFence Info size mismatch");
    }
    else
    {
        ChiNodeWrapper* pNode   = static_cast<ChiNodeWrapper*>(hChiSession);
        // do we need to convert result?
        result = pNode->GetChiContext()->CreateChiFence(pInfo, phChiFence);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNReleaseFence
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNReleaseFence(
    CHIHANDLE       hChiSession,
    CHIFENCEHANDLE  hChiFence)
{
    CAMX_ASSERT(NULL != hChiSession);

    CDKResult   result  = CDKResultSuccess;
    ChiFence*   pChiFence   = static_cast<ChiFence*>(hChiFence);
    if (NULL != pChiFence)
    {
        ChiNodeWrapper* pNode   = static_cast<ChiNodeWrapper*>(hChiSession);
        result = pNode->GetChiContext()->ReleaseChiFence(hChiFence);
    }
    else
    {
        result = CDKResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupChi, "hChiFence is Invalid");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetDataSource
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNGetDataSource(
    CHIHANDLE            hChiSession,
    CHIDATASOURCE*       pDataSource,
    CHIDATASOURCECONFIG* pDataSourceConfig)
{
    CAMX_UNREFERENCED_PARAM(hChiSession);

    CamxResult              result               = CDKResultSuccess;
    CHINCSDATASOURCECONFIG* pNCSDataSourceConfig = NULL;
    NCSSensorConfig         lNCSConfig           = { 0 };
    BOOL                    isSensorValid        = TRUE;

    if ((NULL != pDataSource) && (NULL != pDataSourceConfig))
    {
        switch (pDataSourceConfig->sourceType)
        {
            case ChiDataGyro:
                pNCSDataSourceConfig = reinterpret_cast<CHINCSDATASOURCECONFIG*>(pDataSourceConfig->pConfig);
                lNCSConfig.sensorType = NCSGyroType;
                break;
            case ChiDataAccel:
                pNCSDataSourceConfig = reinterpret_cast<CHINCSDATASOURCECONFIG*>(pDataSourceConfig->pConfig);
                lNCSConfig.sensorType = NCSAccelerometerType;
                break;
            default:
                CAMX_LOG_ERROR(CamxLogGroupCore, "Unsupported data source");
                result = CDKResultEUnsupported;
                lNCSConfig.sensorType = NCSMaxType;
                isSensorValid = FALSE;
                break;
        }

        if (TRUE == isSensorValid)
        {
            NCSSensor* pSensorObject = NULL;

            CAMX_ASSERT_MESSAGE(pNCSDataSourceConfig->size == sizeof(CHINCSDATASOURCECONFIG),
                                "Data Source config structure of invalid size !!");

            lNCSConfig.operationMode = pNCSDataSourceConfig->operationMode;
            lNCSConfig.reportRate    = pNCSDataSourceConfig->reportRate;
            lNCSConfig.samplingRate  = pNCSDataSourceConfig->samplingRate;

            pSensorObject = SetupNCSLink(&lNCSConfig);
            if (NULL != pSensorObject)
            {
                pDataSource->dataSourceType = pDataSourceConfig->sourceType;
                pDataSource->pHandle        = static_cast<VOID*>(pSensorObject);
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "Setup NCS link failed");
                result               = CDKResultEFailed;
                pSensorObject        = NULL;
                pDataSource->pHandle = NULL;
            }
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "Invalid pointer to the data source pointer!!");
        result               = CDKResultEInvalidArg;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* ChiNodeWrapper::FNGetData(
    CHIHANDLE            hChiSession,
    CHIDATASOURCEHANDLE  hDataSourceHandle,
    CHIDATAREQUEST*      pDataRequest,
    UINT*                pSize)
{
    VOID* pData = NULL;

    CAMX_ASSERT_MESSAGE((NULL != hChiSession) && (NULL != hDataSourceHandle) && (NULL != pDataRequest),
                        "Invalid input params");

    switch (hDataSourceHandle->dataSourceType)
    {
        case ChiDataGyro:
        case ChiDataAccel:
            pData = FNGetDataNCS(hChiSession, hDataSourceHandle, pDataRequest, pSize);
            break;
        case ChiTuningManager:
            pData = FNGetTuningmanager(hChiSession);
            break;
        default:
            CAMX_LOG_ERROR(CamxLogGroupChi, "Unsupported data source type");
            break;
    }

    return pData;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNPutData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNPutData(
    CHIHANDLE            hChiSession,
    CHIDATASOURCEHANDLE  hDataSourceHandle,
    CHIDATAHANDLE        hData)
{
    CamxResult result        = CamxResultSuccess;
    NCSSensor* pSensorObject = NULL;

    CAMX_ASSERT_MESSAGE((NULL != hChiSession) && (NULL != hDataSourceHandle),
                        "Invalid input params");

    if ((NULL != hDataSourceHandle->pHandle) && (NULL != hData))
    {
        switch (hDataSourceHandle->dataSourceType)
        {
            case ChiDataGyro:
            case ChiDataAccel:
                pSensorObject = static_cast<NCSSensor*>(hDataSourceHandle->pHandle);
                result = pSensorObject->PutBackDataObj(
                    static_cast<NCSSensorData*>(hData));
                break;
            default:
                CAMX_LOG_ERROR(CamxLogGroupChi, "Unsupported data source type");
                result = CamxResultEUnsupported;
                break;
        }
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_LOG_ERROR(CamxLogGroupChi, "Invalid accessor object");
    }

    return CamxResultToCDKResult(result);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetDataNCS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* ChiNodeWrapper::FNGetDataNCS(
    CHIHANDLE            hChiSession,
    CHIDATASOURCEHANDLE  hDataSourceHandle,
    CHIDATAREQUEST*      pDataRequest,
    UINT*                pSize)
{
    NCSSensor*      pSensorObject      = NULL;
    NCSSensorData*  pSensorDataObject  = NULL;
    ChiNodeWrapper* pNode              = NULL;
    VOID*           pData              = NULL;
    ChiFence*       pChiFence          = NULL;
    CamxResult      result             = CamxResultSuccess;
    ChiNCSDataRequest* pNCSDataRequest = NULL;

    pNode = static_cast<ChiNodeWrapper*>(hChiSession);

    if (NULL != pDataRequest)
    {
        pNCSDataRequest = reinterpret_cast<ChiNCSDataRequest*>(pDataRequest->hRequestPd);

        switch (pDataRequest->requestType)
        {
            case ChiFetchData:
                pSensorObject = static_cast<NCSSensor*>(hDataSourceHandle->pHandle);
                if ((NULL != pNCSDataRequest) && (pNCSDataRequest->size == sizeof(ChiNCSDataRequest)))
                {
                    if (NULL != pSensorObject)
                    {
                        /// Just get N Recent samples
                        if ( 0 < pNCSDataRequest->numSamples)
                        {
                            pData = pSensorObject->GetLastNSamples(pNCSDataRequest->numSamples);
                        }
                        /// ASynchrounous windowed request
                        else if (NULL != pNCSDataRequest->hChiFence)
                        {
                            pChiFence = static_cast<ChiFence*>(pNCSDataRequest->hChiFence);
                            result = pSensorObject->GetDataAsync(
                                pNCSDataRequest->windowRequest.tStart,
                                pNCSDataRequest->windowRequest.tEnd,
                                pChiFence);
                            pNCSDataRequest->result = CamxResultToCDKResult(result);
                        }
                        /// Synchrounous windowed request
                        else
                        {
                            pData = pSensorObject->GetDataSync(
                                pNCSDataRequest->windowRequest.tStart,
                                pNCSDataRequest->windowRequest.tEnd);
                        }

                        // Populate size if pData is valid
                        if ((NULL != pSize) && (NULL != pData))
                        {
                            *pSize = static_cast<NCSSensorData*>(pData)->GetNumSamples();
                        }
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupChi, "Invalid sensor object");
                        pData = NULL;
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Invalid request payload");
                    pData = NULL;
                }
                break;

            case ChiIterateData:
                pSensorDataObject = static_cast<NCSSensorData*>(hDataSourceHandle->pHandle);
                if (NULL != pSensorDataObject)
                {
                    if (0 <= pDataRequest->index)
                    {
                        pSensorDataObject->SetIndex(pDataRequest->index);
                        pData = pSensorDataObject->GetCurrent();
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupChi, "Invalid index to iterate");
                        pData = NULL;
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Sensor object is NULL");
                    pData = NULL;
                }
                break;
            default:
                break;
        }

    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Invalid input request pointer");
    }

    return pData;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNPutDataSource
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNPutDataSource(
    CHIHANDLE           hChiSession,
    CHIDATASOURCEHANDLE hDataSourceHandle)
{
    CDKResult          result     = CDKResultSuccess;

    CAMX_ASSERT_MESSAGE((NULL != hChiSession) || (NULL != hDataSourceHandle),
                        "Invalid input params");

    CAMX_LOG_DEBUG(CamxLogGroupChi, "Unregister");

    switch (hDataSourceHandle->dataSourceType)
    {
        case ChiDataGyro:
        case ChiDataAccel:
            result = CamxResultToCDKResult(DestroyNCSLink(hDataSourceHandle->pHandle));
            break;
        default:
            CAMX_LOG_ERROR(CamxLogGroupChi, "Unsupported data source type");
            result = CDKResultSuccess;
            break;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNWaitFenceAsync
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNWaitFenceAsync(
    CHIHANDLE               hChiSession,
    PFNCHIFENCECALLBACK     pCallbackFn,
    CHIFENCEHANDLE          hChiFence,
    VOID*                   pData)
{
    CamxResult result = CamxResultEFailed;

    CAMX_ASSERT((NULL != hChiSession) && (NULL != pData) && (NULL != hChiFence) && (NULL != pCallbackFn));

    ChiNodeWrapper* pNode = static_cast<ChiNodeWrapper*>(hChiSession);

    result = pNode->GetChiContext()->WaitChiFence(hChiFence, pCallbackFn, pData);
    if (CamxResultSuccess != result)
    {
        result = CDKResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNSignalFence
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNSignalFence(
    CHIHANDLE               hChiSession,
    CHIFENCEHANDLE          hChiFence,
    CDKResult               statusResult)
{
    CamxResult result = CamxResultEFailed;

    CAMX_ASSERT((NULL != hChiSession) && (NULL != hChiFence));

    ChiNodeWrapper* pNode = static_cast<ChiNodeWrapper*>(hChiSession);

    result = pNode->GetChiContext()->SignalChiFence(hChiFence,
                                                    CDKResultToCamxResult(statusResult));
    if (CamxResultSuccess != result)
    {
        result = CDKResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FNGetFenceStatus
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::FNGetFenceStatus(
    CHIHANDLE               hChiSession,
    CHIFENCEHANDLE          hChiFence,
    CDKResult*              pFenceResult)
{
    CDKResult     result     = CDKResultEFailed;
    CDKResult     lResult;


    CAMX_ASSERT((NULL != hChiSession) && (NULL != hChiFence) && (NULL != pFenceResult));

    ChiNodeWrapper* pNode = static_cast<ChiNodeWrapper*>(hChiSession);

    result = pNode->GetChiContext()->GetChiFenceResult(hChiFence, &lResult);
    if (CamxResultSuccess != result)
    {
        result        = CDKResultEFailed;
        *pFenceResult = CDKResultEInvalidState;
    }
    else
    {
        if (CDKResultSuccess == lResult)
        {
            *pFenceResult = CDKResultSuccess;
        }
        else if (CDKResultEFailed == lResult)
        {
            *pFenceResult = CDKResultEFailed;
        }
        else
        {
            *pFenceResult = CDKResultEInvalidState;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::GetMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::GetMetadata(
    CHIMETADATAINFO* pMetadataInfo)
{
    CDKResult result  = CDKResultSuccess;
    CAMX_ASSERT(NULL != pMetadataInfo);

    if (pMetadataInfo->metadataType == ChiMetadataDynamic)
    {
        UINT64* pOffset = static_cast<UINT64 *>(CAMX_CALLOC(sizeof(UINT64) * pMetadataInfo->tagNum));
        VOID**  ppData  = static_cast<VOID **>(CAMX_CALLOC(sizeof(VOID *) * pMetadataInfo->tagNum));
        BOOL*   pNegate = static_cast<BOOL *>(CAMX_CALLOC(sizeof(BOOL) * pMetadataInfo->tagNum));

        if (NULL == pOffset|| NULL == ppData || NULL == pNegate)
        {
            result = CDKResultENoMemory;
        }

        if (CDKResultSuccess == result)
        {
            for (UINT32 i = 0; i < pMetadataInfo->tagNum; i++)
            {
                INT64 requestTemp = static_cast<INT64>(GetCurrentRequest()) -
                                    static_cast<INT64>(pMetadataInfo->pTagData[i].requestId);

                if (pMetadataInfo->pTagData[i].requestId <= GetCurrentRequest())
                {
                    pOffset[i] = static_cast<UINT64>(requestTemp);
                    pNegate[i] = FALSE;
                }
                else
                {
                    pOffset[i] = static_cast<UINT64>(-1 * requestTemp);
                    pNegate[i] = TRUE;
                }
            }

            GetDataListFromPipeline(pMetadataInfo->pTagList,
                                    ppData,
                                    pOffset,
                                    pMetadataInfo->tagNum,
                                    pNegate,
                                    GetPipeline()->GetPipelineId());

            for (UINT32 i = 0; i < pMetadataInfo->tagNum; i++)
            {
                pMetadataInfo->pTagData[i].pData = ppData[i];
            }
        }

        if (NULL != ppData)
        {
            CAMX_FREE(ppData);
            ppData = NULL;
        }

        if (NULL != pOffset)
        {
            CAMX_FREE(pOffset);
            pOffset = NULL;
        }

        if (NULL != pNegate)
        {
            CAMX_FREE(pNegate);
            pNegate = NULL;
        }
    }
    else if (pMetadataInfo->metadataType == ChiMetadataStatic)
    {
        UINT32  tagNum = pMetadataInfo->tagNum;
        for (UINT32 i = 0; i < tagNum; i++)
        {
            CHITAGDATA* pTagData = &pMetadataInfo->pTagData[i];
            HAL3MetadataUtil::GetMetadata(const_cast<Metadata *>(m_pStaticMetadata),
                                          pMetadataInfo->pTagList[i],
                                          &pTagData->pData);
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "Unsupported metadatatype %d", pMetadataInfo->metadataType);
        result = CDKResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::SetMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::SetMetadata(
    CHIMETADATAINFO* pMetadataInfo)
{
    UINT32 tagNum       = pMetadataInfo->tagNum;
    CDKResult result    = CDKResultSuccess;

    UINT*         pDataSize = static_cast<UINT *>(CAMX_CALLOC(sizeof(UINT) * tagNum));
    const VOID**  ppData    = static_cast<const VOID **>(CAMX_CALLOC(sizeof(VOID *) * tagNum));

    if (NULL == pDataSize|| NULL == ppData)
    {
        result = CDKResultENoMemory;
    }

    if (CDKResultSuccess == result)
    {
        for (UINT32 i = 0; i < pMetadataInfo->tagNum; i++)
        {
            ppData[i]    = pMetadataInfo->pTagData[i].pData;
            pDataSize[i] = static_cast<UINT>(pMetadataInfo->pTagData[i].dataSize);
        }

        WriteDataList(pMetadataInfo->pTagList, ppData, pDataSize, tagNum);
    }

    if (NULL != ppData)
    {
        CAMX_FREE(ppData);
        ppData = NULL;
    }

    if (NULL != pDataSize)
    {
        CAMX_FREE(pDataSize);
        pDataSize = NULL;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::GetVendorTagBase
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::GetVendorTagBase(
    CHIVENDORTAGBASEINFO* pVendorTagBaseInfo)
{
    CAMX_UNREFERENCED_PARAM(pVendorTagBaseInfo);
    return CDKResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ProcRequestDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::ProcRequestDone(
    CHINODEPROCESSREQUESTDONEINFO* pInfo)
{
    CAMX_UNREFERENCED_PARAM(pInfo);

    UINT32 requestIdIndex = (pInfo->frameNum % MaxRequestQueueDepth);

    for (UINT i = 0; i < m_perRequestData[requestIdIndex].numFences; i++)
    {
        if (NULL != m_perRequestData[requestIdIndex].phFence[i])
        {
            CSLFenceSignal(*m_perRequestData[requestIdIndex].phFence[i], CSLFenceResultSuccess);
        }
    }

    if (FALSE == pInfo->isEarlyMetadataDone)
    {
        ProcessMetadataDone(pInfo->frameNum);
    }

    if (TRUE == IsSinkNoBufferNode())
    {
        ProcessRequestIdDone(pInfo->frameNum);
    }

    return CDKResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ProcMetadataDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::ProcMetadataDone(
    CHINODEPROCESSMETADATADONEINFO* pInfo)
{
    ProcessMetadataDone(pInfo->frameNum);
    return CDKResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ProcessingNodeFinalizeInitialization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::ProcessingNodeFinalizeInitialization(
    FinalizeInitializationData*  pFinalizeInitializationData)
{
    CAMX_UNREFERENCED_PARAM(pFinalizeInitializationData);

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::GetChiNodeCapsMask
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDKResult ChiNodeWrapper::GetChiNodeCapsMask(
    const NodeCreateInputData*  pCreateInputData,
    CHINODECREATEINFO*          pNodeCreateInfo)
{
    UINT32 propertyCount = pCreateInputData->pNodeInfo->nodePropertyCount;
    CDKResult result    = CDKResultSuccess;

    if (0 == OsUtils::StrCmp(m_pComponentName, "com.qti.node.gpu"))
    {
        CAMX_LOG_VERBOSE(CamxLogGroupChi, "nodePropertyCount %d", pCreateInputData->pNodeInfo->nodePropertyCount);

        for (UINT32 count = 0; count < propertyCount; count++)
        {
            if (NodePropertyGPUCapsMaskType == pCreateInputData->pNodeInfo->pNodeProperties[count].id)
            {
                if (NULL != pCreateInputData->pNodeInfo->pNodeProperties[count].pValue)
                {
                    m_instancePropertyCapsMask = static_cast<UINT>(
                                                 atoi(static_cast<const CHAR*>(
                                                 pCreateInputData->pNodeInfo->pNodeProperties[count].pValue)));
                }
                else
                {
                    result = CamxResultEInvalidPointer;
                    CAMX_LOG_ERROR(CamxLogGroupChi, "NodeProperties pValue is NUll");
                }
            }
        }

        switch (m_instancePropertyCapsMask)
        {
            case ChiNodeCapsScale:
            case ChiNodeCapsGpuMemcpy:
            case ChiNodeCapsGPUGrayscale:
            case ChiNodeCapsGPURotate:
            case ChiNodeCapsGPUDownscale:
            case ChiNodeCapsGPUFlip:
                pNodeCreateInfo->nodeCaps.nodeCapsMask = m_instancePropertyCapsMask;
                break;
            default:
                pNodeCreateInfo->nodeCaps.nodeCapsMask = ChiNodeCapsGpuMemcpy;
                break;
        }
        CAMX_LOG_INFO(CamxLogGroupCore, "GPU node capsMask is 0x%x", pNodeCreateInfo->nodeCaps.nodeCapsMask);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ProcessingNodeInitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::ProcessingNodeInitialize(
    const NodeCreateInputData*   pCreateInputData,
    NodeCreateOutputData*        pCreateOutputData)
{
    CamxResult          result         = CamxResultSuccess;
    CHINODECREATEINFO   nodeCreateInfo = {0};

    ChiCameraInfo cameraInfo           = {0};
    CameraInfo    legacyCameraInfo;

    cameraInfo.pLegacy  = static_cast<VOID*>(&legacyCameraInfo);
    result              = m_pChiContext->GetCameraInfo(GetPipeline()->GetCameraId(), &cameraInfo);
    if (CamxResultSuccess == result)
    {
        m_pStaticMetadata = legacyCameraInfo.pStaticCameraInfo;
        CAMX_LOG_INFO(CamxLogGroupCore,
                      "device version %d static info %p",
                      legacyCameraInfo.deviceVersion,
                      legacyCameraInfo.pStaticCameraInfo);
        CAMX_ASSERT(NULL != m_pStaticMetadata);
    }

    if (pCreateInputData->pNodeInfo->nodePropertyCount > 0)
    {
        // Expecting the component name to always be the first property with an id equal to 1.
        const CHAR* pComponentName = static_cast<CHAR*>(pCreateInputData->pNodeInfo->pNodeProperties[0].pValue);
        CAMX_ASSERT(pCreateInputData->pNodeInfo->pNodeProperties[0].id == NodePropertyCustomLib);

        SIZE_T componentNameLen = OsUtils::StrLen(pComponentName) + 1;
        m_pComponentName = static_cast<CHAR*>(CAMX_CALLOC(componentNameLen));

        if (NULL != m_pComponentName)
        {
            OsUtils::StrLCpy(m_pComponentName, pComponentName, componentNameLen);
        }
        else
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT_ALWAYS();
        }
    }

    if (CamxResultSuccess == result)
    {
        ChiNodeInterface nodeInterface;

        nodeInterface.size                  = sizeof(ChiNodeInterface);
        nodeInterface.majorVersion          = 0;
        nodeInterface.minorVersion          = 0;
        nodeInterface.pGetMetadata          = FNGetMetadata;
        nodeInterface.pSetMetadata          = FNSetMetadata;
        nodeInterface.pGetVendorTagBase     = FNGetVendorTagBase;
        nodeInterface.pProcessRequestDone   = FNProcRequestDone;
        nodeInterface.pCreateFence          = FNCreateFence;
        nodeInterface.pReleaseFence         = FNReleaseFence;
        nodeInterface.pWaitFenceAsync       = FNWaitFenceAsync;
        nodeInterface.pSignalFence          = FNSignalFence;
        nodeInterface.pGetFenceStatus       = FNGetFenceStatus;
        nodeInterface.pGetDataSource        = FNGetDataSource;
        nodeInterface.pPutDataSource        = FNPutDataSource;
        nodeInterface.pGetData              = FNGetData;
        nodeInterface.pPutData              = FNPutData;
        nodeInterface.pCacheOps             = FNCacheOps;
        nodeInterface.pProcessMetadataDone  = FNProcMetadataDone;
        m_nodeCallbacks.pChiNodeSetNodeInterface(&nodeInterface);
    }

    if ((NULL != m_nodeCallbacks.pCreate) && (CamxResultSuccess == result))
    {
        nodeCreateInfo.size                   = sizeof(nodeCreateInfo);
        nodeCreateInfo.hChiSession            = static_cast<CHIHANDLE>(this);
        nodeCreateInfo.phNodeSession          = NULL;
        nodeCreateInfo.nodeId                 = pCreateInputData->pNodeInfo->nodeId;
        nodeCreateInfo.nodeInstanceId         = pCreateInputData->pNodeInfo->instanceId;
        nodeCreateInfo.nodeFlags.isRealTime   = IsRealTime();
        nodeCreateInfo.nodeFlags.isBypassable = IsBypassableNode();
        /// @todo (CAMX-1806) fill the node caps
        /// nodeCreateInfo.nodeCaps = xx;

        GetChiNodeCapsMask(pCreateInputData, &nodeCreateInfo);

        CDKResult cdkResult = m_nodeCallbacks.pCreate(&nodeCreateInfo);
        result              = CDKResultToCamxResult(cdkResult);
    }

    if (CamxResultSuccess == result)
    {
        pCreateOutputData->createFlags.canDRQPreemptOnStopRecording = nodeCreateInfo.nodeFlags.canDRQPreemptOnStopRecording;
        m_canNodeSetBufferDependency                                = nodeCreateInfo.nodeFlags.canSetInputBufferDependency;
        m_hNodeSession                                              = nodeCreateInfo.phNodeSession;

        if (NULL != m_nodeCallbacks.pGetCapabilities)
        {
            m_nodeCapsMask.size = sizeof(CHINODECAPSINFO);
            m_nodeCallbacks.pGetCapabilities(&m_nodeCapsMask);
            if (m_nodeCapsMask.nodeCapsMask & ChiNodeCapsParallelReq)
            {
                m_parallelProcessRequests = TRUE;
                CAMX_LOG_INFO(CamxLogGroupChi, "Node supports parallel process request\n");
            }
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ProcessingNodeFinalizeInputRequirement
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::ProcessingNodeFinalizeInputRequirement(
    BufferNegotiationData* pBufferNegotiationData)
{
    CAMX_ASSERT(NULL != pBufferNegotiationData);

    CamxResult                   result                                                  = CamxResultSuccess;
    OutputPortNegotiationData*   pOutputPortNegotiationData                              = NULL;
    ChiNodeQueryBufferInfo       chiNodeQueryBufferInfo                                  = {0};
    UINT32                       totalInputPorts                                         = 0;
    UINT32                       totalOutputPorts                                        = 0;
    UINT32                       inputPortId[MaximumChiNodeWrapperInputPorts]            = {};
    ChiInputPortQueryBufferInfo  nodeInputPortOptions[MaximumChiNodeWrapperInputPorts]   = {};
    ChiOutputPortQueryBufferInfo outputPortInfo[MaximumChiNodeWrapperOutputPorts]        = {};
    ChiNodeBufferRequirement     outputPortRequirement[MaximumChiNodeWrapperOutputPorts][MaximumChiNodeWrapperInputPorts] = {};

    // Get Input Port List
    GetAllInputPortIds(&totalInputPorts, &inputPortId[0]);
    totalOutputPorts = pBufferNegotiationData->numOutputPortsNotified;

    chiNodeQueryBufferInfo.size                 = sizeof(CHINODEQUERYBUFFERINFO);
    chiNodeQueryBufferInfo.hNodeSession         = m_hNodeSession;
    chiNodeQueryBufferInfo.numOutputPorts       = totalOutputPorts;
    chiNodeQueryBufferInfo.numInputPorts        = totalInputPorts;
    chiNodeQueryBufferInfo.pInputOptions        = &nodeInputPortOptions[0];
    chiNodeQueryBufferInfo.pOutputPortQueryInfo = &outputPortInfo[0];

    for (UINT outputIndex = 0; outputIndex < totalOutputPorts && outputIndex < MaximumChiNodeWrapperOutputPorts; outputIndex++)
    {
        pOutputPortNegotiationData = &pBufferNegotiationData->pOutputPortNegotiationData[outputIndex];

        chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].outputPortId           =
            GetOutputPortId(pOutputPortNegotiationData->outputPortIndex);
        chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].numConnectedInputPorts =
            pOutputPortNegotiationData->numInputPortsNotification;

        chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement = &outputPortRequirement[outputIndex][0];

        for (UINT inputIndex = 0; inputIndex < pOutputPortNegotiationData->numInputPortsNotification; inputIndex++)
        {
            chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement[inputIndex].minW     =
                pOutputPortNegotiationData->inputPortRequirement[inputIndex].minWidth;
            chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement[inputIndex].minH     =
                pOutputPortNegotiationData->inputPortRequirement[inputIndex].minHeight;
            chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement[inputIndex].maxW     =
                pOutputPortNegotiationData->inputPortRequirement[inputIndex].maxWidth;
            chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement[inputIndex].maxH     =
                pOutputPortNegotiationData->inputPortRequirement[inputIndex].maxHeight;
            chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement[inputIndex].optimalW =
                pOutputPortNegotiationData->inputPortRequirement[inputIndex].optimalWidth;
            chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].pBufferRequirement[inputIndex].optimalH =
                pOutputPortNegotiationData->inputPortRequirement[inputIndex].optimalHeight;
        }
    }

    for (UINT inputIndex = 0; inputIndex < chiNodeQueryBufferInfo.numInputPorts; inputIndex++)
    {
        ChiInputPortQueryBufferInfo* pInputOptions = &chiNodeQueryBufferInfo.pInputOptions[inputIndex];

        pInputOptions->inputPortId = inputPortId[inputIndex];
    }

    // call into the chi node using the QueryBufferInfo to query for the requirement of input buffer
    m_nodeCallbacks.pQueryBufferInfo(&chiNodeQueryBufferInfo);

    // Sink no buffer node doesn't have any buffer to output, so skip the logic
    if (FALSE == IsSinkNoBufferNode())
    {
        for (UINT outputIndex = 0; outputIndex < pBufferNegotiationData->numOutputPortsNotified; outputIndex++)
        {
            ChiNodeBufferRequirement* pOutputOption =
                &chiNodeQueryBufferInfo.pOutputPortQueryInfo[outputIndex].outputBufferOption;

            pOutputPortNegotiationData = &pBufferNegotiationData->pOutputPortNegotiationData[outputIndex];

            // Store the buffer requirements for this output port which will be reused to set, during forward walk.
            // The values stored here could be final output dimensions unless it is overridden by forward walk.
            pOutputPortNegotiationData->outputBufferRequirementOptions.optimalWidth  = pOutputOption->optimalW;
            pOutputPortNegotiationData->outputBufferRequirementOptions.optimalHeight = pOutputOption->optimalH;
            pOutputPortNegotiationData->outputBufferRequirementOptions.minWidth      = pOutputOption->minW;
            pOutputPortNegotiationData->outputBufferRequirementOptions.minHeight     = pOutputOption->minH;
            pOutputPortNegotiationData->outputBufferRequirementOptions.maxWidth      = pOutputOption->maxW;
            pOutputPortNegotiationData->outputBufferRequirementOptions.maxHeight     = pOutputOption->maxH;

            CAMX_STATIC_ASSERT(sizeof(AlignmentInfo) == sizeof(ChiAlignmentInfo));
            Utils::Memcpy(&pOutputPortNegotiationData->outputBufferRequirementOptions.planeAlignment[0],
                          &pOutputOption->planeAlignment[0],
                          sizeof(AlignmentInfo) * FormatsMaxPlanes);

            if ((pOutputOption->optimalW == 0) ||
                (pOutputOption->optimalH == 0) ||
                (pOutputOption->minW     == 0) ||
                (pOutputOption->minH     == 0) ||
                (pOutputOption->maxW     == 0) ||
                (pOutputOption->maxH     == 0))
            {
                result = CamxResultEFailed;
                break;
            }
        }
    }

    if (result == CamxResultEFailed)
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "ERROR: Buffer Negotiation Failed!\n");
    }
    else
    {
        pBufferNegotiationData->numInputPorts = totalInputPorts;

        for (UINT input = 0; input < totalInputPorts; input++)
        {
            ChiInputPortQueryBufferInfo* pInput = &chiNodeQueryBufferInfo.pInputOptions[input];

            pBufferNegotiationData->inputBufferOptions[input].nodeId     = Type();
            pBufferNegotiationData->inputBufferOptions[input].instanceId = InstanceID();
            pBufferNegotiationData->inputBufferOptions[input].portId     = pInput->inputPortId;

            BufferRequirement* pInputBufferRequirement = &pBufferNegotiationData->inputBufferOptions[input].bufferRequirement;

            pInputBufferRequirement->optimalWidth  = pInput->inputBufferOption.optimalW;
            pInputBufferRequirement->optimalHeight = pInput->inputBufferOption.optimalH;
            pInputBufferRequirement->minWidth      = pInput->inputBufferOption.minW;
            pInputBufferRequirement->minHeight     = pInput->inputBufferOption.minH;
            pInputBufferRequirement->maxWidth      = pInput->inputBufferOption.maxW;
            pInputBufferRequirement->maxHeight     = pInput->inputBufferOption.maxH;

            CAMX_STATIC_ASSERT(sizeof(AlignmentInfo) == sizeof(ChiAlignmentInfo));
            Utils::Memcpy(&pInputBufferRequirement->planeAlignment[0],
                          &pInput->inputBufferOption.planeAlignment[0],
                          sizeof(AlignmentInfo) * FormatsMaxPlanes);

            CAMX_LOG_INFO(CamxLogGroupChi,
                          "Buffer Negotiation dims, Port %d Optimal %d x %d, Min %d x %d, Max %d x %d\n",
                          pInput->inputPortId,
                          pInputBufferRequirement->optimalWidth,
                          pInputBufferRequirement->optimalHeight,
                          pInputBufferRequirement->minWidth,
                          pInputBufferRequirement->minHeight,
                          pInputBufferRequirement->maxWidth,
                          pInputBufferRequirement->maxHeight);
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::FinalizeBufferProperties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID ChiNodeWrapper::FinalizeBufferProperties(
    BufferNegotiationData* pBufferNegotiationData)
{
    UINT32                      width                       = 0;
    UINT32                      height                      = 0;
    InputPortNegotiationData*   pInputPortNegotiationData   = NULL;

    CAMX_ASSERT(NULL != pBufferNegotiationData);

    pInputPortNegotiationData = &pBufferNegotiationData->pInputPortNegotiationData[0];

    for (UINT index = 0; index < pBufferNegotiationData->numOutputPortsNotified; index++)
    {
        OutputPortNegotiationData* pOutputPortNegotiationData   = &pBufferNegotiationData->pOutputPortNegotiationData[index];
        InputPortNegotiationData*  pInputPortNegotiationData    = &pBufferNegotiationData->pInputPortNegotiationData[0];
        BufferProperties*          pFinalOutputBufferProperties = pOutputPortNegotiationData->pFinalOutputBufferProperties;
        CHINODESETBUFFERPROPERTIESINFO  bufferInfo      = {0};
        CHINODEIMAGEFORMAT              bufferFormat    = {0};

        // Option-1: Check the node's capability in ChiNodeWrapper and decide based on the capability whether it can scale.
        // Option-2: Pass all of the parameters from BufferNegotiationData to the ChiNode and let it decide the output.
        // Option-2 needs interface changes, so Option-1 can be considered for Phase-1 and Option-2 is Phase-2.

        CAMX_LOG_INFO(CamxLogGroupChi, "nodeCapsMask : %d, ChiNodeCapsScale:%d",
            m_nodeCapsMask.nodeCapsMask, ChiNodeCapsScale);

        if (m_nodeCapsMask.nodeCapsMask & ChiNodeCapsScale)
        {
            width = pOutputPortNegotiationData->outputBufferRequirementOptions.optimalWidth;
            height = pOutputPortNegotiationData->outputBufferRequirementOptions.optimalHeight;
        }
        else
        {
            width = pInputPortNegotiationData->pImageFormat->width;
            height = pInputPortNegotiationData->pImageFormat->height;
        }

        if ((FALSE == IsSinkNoBufferNode()) &&
            (FALSE == IsSinkPortWithBuffer(pOutputPortNegotiationData->outputPortIndex)) &&
            (FALSE == IsNonSinkHALBufferOutput(pOutputPortNegotiationData->outputPortIndex)))
        {
            UINT outputPortId = GetOutputPortId(pOutputPortNegotiationData->outputPortIndex);

            switch (outputPortId)
            {
                case ChiNodeOutputFull:
                    pFinalOutputBufferProperties->imageFormat.width  = width;
                    pFinalOutputBufferProperties->imageFormat.height = height;
                    break;

                case ChiNodeOutputDS4:
                    pFinalOutputBufferProperties->imageFormat.width  =
                        Utils::EvenCeilingUINT32(Utils::AlignGeneric32(width, 4) / 4);
                    pFinalOutputBufferProperties->imageFormat.height =
                        Utils::EvenCeilingUINT32(Utils::AlignGeneric32(height, 4) / 4);
                    break;

                case ChiNodeOutputDS16:
                    pFinalOutputBufferProperties->imageFormat.width  =
                        Utils::EvenCeilingUINT32(Utils::AlignGeneric32(width, 16) / 16);
                    pFinalOutputBufferProperties->imageFormat.height =
                        Utils::EvenCeilingUINT32(Utils::AlignGeneric32(height, 16) / 16);

                    break;

                case ChiNodeOutputDS64:
                    pFinalOutputBufferProperties->imageFormat.width  =
                        Utils::EvenCeilingUINT32(Utils::AlignGeneric32(width, 64) / 64);
                    pFinalOutputBufferProperties->imageFormat.height =
                        Utils::EvenCeilingUINT32(Utils::AlignGeneric32(height, 64) / 64);

                    break;

                default:
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Unsupported port %d", outputPortId);
                    break;
            }

            Utils::Memcpy(&pFinalOutputBufferProperties->imageFormat.planeAlignment[0],
                          &pOutputPortNegotiationData->outputBufferRequirementOptions.planeAlignment[0],
                          sizeof(AlignmentInfo) * FormatsMaxPlanes);
        }

        bufferInfo.hNodeSession = m_hNodeSession;
        bufferInfo.size         = sizeof(CHINODESETBUFFERPROPERTIESINFO);
        bufferInfo.portId       = pOutputPortNegotiationData->outputPortIndex;
        bufferInfo.pFormat      = &bufferFormat;

        bufferFormat.width      = width;
        bufferFormat.height     = height;

        CAMX_LOG_INFO(CamxLogGroupChi, "index %d portId %d bufferInfo %d x %d",
                      index, bufferInfo.portId, bufferFormat.width, bufferFormat.height);

        if (NULL != m_nodeCallbacks.pSetBufferInfo)
        {
            m_nodeCallbacks.pSetBufferInfo(&bufferInfo);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::PostPipelineCreate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::PostPipelineCreate()
{
    CDKResult cdkResult = CDKResultSuccess;

    if (NULL != m_nodeCallbacks.pPostPipelineCreate)
    {
        cdkResult = m_nodeCallbacks.pPostPipelineCreate(m_hNodeSession);
    }
    else if (NULL != m_nodeCallbacks.pPipelineCreated)
    {
        // This is to maintain backward compatibility for now.
        // pPipelineCreated is deprecated and will be removed soon
        CAMX_LOG_WARN(CamxLogGroupChi, "pPipelineCreated is deprecated, use pPostPipelineCreate");

        m_nodeCallbacks.pPipelineCreated(m_hNodeSession);
    }

    return CDKResultToCamxResult(cdkResult);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ExecuteProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::ExecuteProcessRequest(
    ExecuteProcessRequestData*   pExecuteProcessRequestData)
{

    CAMX_ASSERT(NULL != pExecuteProcessRequestData);
    CAMX_ASSERT(NULL != m_hNodeSession);

    CamxResult                result           = CamxResultSuccess;
    PerRequestActivePorts*    pPerRequestPorts = pExecuteProcessRequestData->pEnabledPortsInfo;
    CHINODEPROCESSREQUESTINFO info             = {0};
    PerRequestOutputPortInfo* pOutputPort      = NULL;
    NodeProcessRequestData*   pNodeRequestData = pExecuteProcessRequestData->pNodeProcessRequestData;
    CHIDEPENDENCYINFO         dependency       = {0};
    UINT32                    requestIdIndex   = 0;

    info.size           = sizeof(info);
    info.hNodeSession   = m_hNodeSession;
    info.frameNum       = pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId;
    info.inputNum       = pPerRequestPorts->numInputPorts;
    info.phInputBuffer  = m_phInputBuffer;
    info.outputNum      = pPerRequestPorts->numOutputPorts;
    info.phOutputBuffer = m_phOutputBuffer;
    info.pBypassData    = m_pBypassData;
    info.pDependency    = &dependency;

    dependency.size                 = sizeof(CHIDEPENDENCYINFO);
    dependency.processSequenceId    = pNodeRequestData->processSequenceId;
    dependency.hNodeSession         = m_hNodeSession;

    CAMX_ASSERT(m_numInputPort >= pPerRequestPorts->numInputPorts);
    CAMX_ASSERT(m_numOutputPort >= pPerRequestPorts->numOutputPorts);

    requestIdIndex = (pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId % MaxRequestQueueDepth);

    if (0 == dependency.processSequenceId)
    {
        m_perRequestData[requestIdIndex].numFences = 0;

        if (FALSE == IsSinkNoBufferNode())
        {
            for (UINT i = 0; i < pPerRequestPorts->numOutputPorts; i++)
            {
                pOutputPort = &pPerRequestPorts->pOutputPorts[i];
                if ((NULL != pOutputPort) && (NULL != pOutputPort->pImageBuffer[0]))
                {
                    m_perRequestData[requestIdIndex].phFence[i] = pOutputPort->phFence;
                    m_perRequestData[requestIdIndex].numFences++;

                    UINT32 index = (info.frameNum *  pPerRequestPorts->numOutputPorts) %
                        (MaxOutputBuffers * pPerRequestPorts->numOutputPorts);
                    m_phOutputBuffer[index + i] =
                        ImageBufferToChiBuffer(pOutputPort->pImageBuffer[0], m_phOutputBuffer, index + i);

                    m_phOutputBuffer[index + i]->portId = pOutputPort->portId;

                    if ((pPerRequestPorts->numOutputPorts - 1) == i)
                    {
                        info.phOutputBuffer = &m_phOutputBuffer[index];
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "%s: Output Port/Image Buffer is Null ", __FUNCTION__);
                    result = CamxResultEInvalidArg;
                }

                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Output Port: Output Port/Image Buffer is Null i %d", i);
                    break;
                }
            }
        }

        // First request always waits for input fences
        for (UINT i = 0; i < pPerRequestPorts->numInputPorts; i++)
        {
            PerRequestInputPortInfo* pInputPort = &pPerRequestPorts->pInputPorts[i];

            if ((NULL != pInputPort) && (NULL != pInputPort->pImageBuffer))
            {
                UINT32 index = (info.frameNum *  pPerRequestPorts->numInputPorts) %
                               (MaxRequestQueueDepth * pPerRequestPorts->numInputPorts);

                m_phInputBuffer[index + i]          = ImageBufferToChiBuffer(pInputPort->pImageBuffer,
                                                                             m_phInputBuffer,
                                                                             index + i);
                m_phInputBuffer[index + i]->portId  = pInputPort->portId;
                if ((pPerRequestPorts->numInputPorts - 1) == i)
                {
                    info.phInputBuffer = &m_phInputBuffer[index];
                }
                result = CamxResultSuccess;
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "%s: Input Port/Image Buffer is Null ", __FUNCTION__);

                result = CamxResultEInvalidArg;
            }

            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "Input Port: Input Port/Image Buffer is Null i %d", i);
                break;
            }

            if (FALSE == IsSourceBufferInputPort(i))
            {
                if ((1 == m_canNodeSetBufferDependency) && (-1 != *pInputPort->phFence))
                {
                    m_phInputBuffer[i]->pfenceHandle     = pInputPort->phFence;
                    m_phInputBuffer[i]->pIsFenceSignaled = pInputPort->pIsFenceSignaled;
                }
                else
                {
                    // Looking at index 0, because we don't have more than one dependency list. Need to update here,
                    // if we plan to change to more than one dependency list.
                    UINT fenceCount = pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount;

                    pNodeRequestData->dependencyInfo[0].bufferDependency.phFences[fenceCount] =
                        pInputPort->phFence;
                    pNodeRequestData->dependencyInfo[0].bufferDependency.pIsFenceSignaled[fenceCount] =
                        pInputPort->pIsFenceSignaled;

                    pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount++;
                }
            }
        }

        if (pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount > 0)
        {
            pNodeRequestData->dependencyInfo[0].dependencyFlags.hasBufferDependency = TRUE;
            pNodeRequestData->dependencyInfo[0].processSequenceId = FenceCompleteProcessSequenceId;
            pNodeRequestData->numDependencyLists = 1;

            CAMX_LOG_VERBOSE(CamxLogGroupChi,
                             "Added buffer dependencies. reqId = %llu Number of fences = %d",
                             pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId,
                             pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount);
        }
    }
    else if (FenceCompleteProcessSequenceId == pNodeRequestData->processSequenceId)
    {
        // Internal callback should result in the CHI node getting 0, as the first time it will be invoked
        // Other (than 0 and -2) sequenceIds are passed through
        dependency.processSequenceId = 0;
    }

    if (0 == pNodeRequestData->numDependencyLists)
    {
        UINT32 outputIndex = (info.frameNum * pPerRequestPorts->numOutputPorts) %
            (MaxOutputBuffers * pPerRequestPorts->numOutputPorts);

        UINT32 inputIndex  = (info.frameNum * pPerRequestPorts->numInputPorts) %
            (MaxRequestQueueDepth * pPerRequestPorts->numInputPorts);

        info.phOutputBuffer = &m_phOutputBuffer[outputIndex];
        info.phInputBuffer  = &m_phInputBuffer[inputIndex];

        CDKResult cdkResult = m_nodeCallbacks.pProcessRequest(&info);
        result              = CDKResultToCamxResult(cdkResult);

        m_perRequestData[requestIdIndex].isDelayedRequestDone = info.isDelayedRequestDone;

        CAMX_LOG_VERBOSE(CamxLogGroupChi, "Property dependency count %d", info.pDependency->count);
        CAMX_LOG_VERBOSE(CamxLogGroupChi, "Buffer dependency count %d", info.pDependency->inputBufferFenceCount);

        CAMX_ASSERT_MESSAGE(0  != info.pDependency->processSequenceId, "0 is a reserved ProcessSequenceId");
        CAMX_ASSERT_MESSAGE(-1 != info.pDependency->processSequenceId, "-1 is a reserved ProcessSequenceId");
        CAMX_ASSERT_MESSAGE(-2 != info.pDependency->processSequenceId, "-2 is a reserved ProcessSequenceId");

        // Satisfy sequential execution dependency if chi node requests for it,
        // else dependency will be satisfied on request done
        if (TRUE == info.pDependency->satisfySequentialExecutionDependency)
        {
            UINT        tag     = GetNodeCompleteProperty();
            const UINT  one     = 1;
            const VOID* pOne[1] = { &one };
            WriteDataList(&tag, pOne, &one, 1);
        }

        // Set a dependency on the completion of the previous ExecuteProcessRequest() call
        // so that we can guarantee serialization of all ExecuteProcessRequest() calls for this node.
        // Do this when Chi node requests for sequential execution
        if (TRUE == info.pDependency->sequentialExecutionNeeded)
        {
            if (FirstValidRequestId < info.frameNum)
            {
                info.pDependency->properties[info.pDependency->count] = GetNodeCompleteProperty();
                info.pDependency->offsets[info.pDependency->count]    = 1;
                info.pDependency->count++;
            }
        }

        if (0 < info.pDependency->count)
        {
            UINT count = 0;

            for (UINT i = 0; i < info.pDependency->count; i++)
            {
                UINT32 tag = info.pDependency->properties[i];

                // check if the dependency's tag is for internal result pool
                if ((0 != (tag & DriverInternalGroupMask)) && (tag != GetNodeCompleteProperty()))
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Only support depending on tag in result pool %x", tag);
                    continue;
                }

                UINT32 propertyID = VendorTagManager::GetMappedPropertyID(tag);
                // if the dependency maps to property, then use the propertyID, otherwise
                // just use the tag id
                pNodeRequestData->dependencyInfo[0].propertyDependency.properties[count]    =
                    propertyID > 0 ? propertyID : tag;
                pNodeRequestData->dependencyInfo[0].propertyDependency.offsets[count]       =
                    info.pDependency->offsets[i];
                pNodeRequestData->dependencyInfo[0].propertyDependency.negate[count]        =
                    info.pDependency->negate[i];

                CAMX_LOG_VERBOSE(CamxLogGroupChi, "vendor tag value %x, mapped property value %x, offset %d",
                                 tag,
                                 pNodeRequestData->dependencyInfo[0].propertyDependency.properties[i],
                                 info.pDependency->offsets[i]);
                count++;
            }

            if (0 < count)
            {
                // Update dependency request data for topology to consume
                pNodeRequestData->dependencyInfo[0].dependencyFlags.hasPropertyDependency = TRUE;
                pNodeRequestData->numDependencyLists                                      = 1;
                pNodeRequestData->dependencyInfo[0].propertyDependency.count              = count;
                pNodeRequestData->dependencyInfo[0].processSequenceId                     = info.pDependency->processSequenceId;
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "All dependencies are invalid!!!");
            }
        }

        if (0 < info.pDependency->inputBufferFenceCount)
        {
            for (UINT i = 0; i < info.pDependency->inputBufferFenceCount; i++)
            {
                UINT fenceCount = pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount;

                pNodeRequestData->dependencyInfo[0].bufferDependency.phFences[fenceCount] =
                    static_cast<CSLFence*>(info.pDependency->pInputBufferFence[i]);
                pNodeRequestData->dependencyInfo[0].bufferDependency.pIsFenceSignaled[fenceCount] =
                    static_cast<UINT*>(info.pDependency->pInputBufferFenceIsSignaled[i]);

                pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount++;
            }

            if (pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount > 0)
            {
                pNodeRequestData->dependencyInfo[0].dependencyFlags.hasBufferDependency = TRUE;
                pNodeRequestData->dependencyInfo[0].processSequenceId = info.pDependency->processSequenceId;
                pNodeRequestData->numDependencyLists = 1;

                CAMX_LOG_VERBOSE(CamxLogGroupChi,
                                 "Added buffer dependencies. reqId = %llu Number of fences = %d",
                                 pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId,
                                 pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount);
            }
        }

        if (0 < info.pDependency->chiFenceCount)
        {
            UINT  fenceCount      = 0;
            VOID* pFenceUserdata  = NULL;

            for (UINT i = 0; i < info.pDependency->chiFenceCount; i++)
            {
                CHIFENCEHANDLE hChiFence = info.pDependency->pChiFences[i];

                // check if the dependency's chi fence is valid
                if (NULL == hChiFence)
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Only support valid chi fences for dependency");
                    continue;
                }

                pNodeRequestData->dependencyInfo[0].chiFenceDependency.pChiFences[i] = static_cast<ChiFence*>(hChiFence);

                CHIFENCECALLBACKINFO* pFenceCallbackInfo =
                    static_cast<CHIFENCECALLBACKINFO*>(CAMX_CALLOC(sizeof(CHIFENCECALLBACKINFO)));

                if (NULL != pFenceCallbackInfo)
                {
                    CAMX_LOG_VERBOSE(CamxLogGroupChi, "depending on chi fence");

                    pFenceCallbackInfo->frameNum          = info.frameNum;
                    pFenceCallbackInfo->processSequenceId = info.pDependency->processSequenceId;
                    pFenceCallbackInfo->hChiSession       = static_cast<CHIHANDLE>(this);
                    pFenceCallbackInfo->size              = sizeof(CHIFENCECALLBACKINFO);
                    pFenceCallbackInfo->pUserData         = GetChiContext();
                    pFenceUserdata                        = pFenceCallbackInfo;
                    fenceCount++;
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Out of memory");
                    break;
                }
            }

            if (0 < fenceCount)
            {
                pNodeRequestData->dependencyInfo[0].dependencyFlags.hasFenceDependency   = TRUE;
                pNodeRequestData->numDependencyLists                                     = 1;
                pNodeRequestData->dependencyInfo[0].chiFenceDependency.chiFenceCount     = fenceCount;
                pNodeRequestData->dependencyInfo[0].chiFenceDependency.pUserData         = pFenceUserdata;
                pNodeRequestData->dependencyInfo[0].chiFenceDependency.pChiFenceCallback = ChiFenceDependencyCallback;
                pNodeRequestData->dependencyInfo[0].processSequenceId                    = info.pDependency->processSequenceId;
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "All dependencies are invalid!!!");
            }
        }

        if ((0 == info.pDependency->count) &&
            (0 == info.pDependency->chiFenceCount) &&
            (0 == info.pDependency->inputBufferFenceCount))
        {
            if (FALSE == info.isDelayedRequestDone)
            {
                if (FALSE == info.isEarlyMetadataDone)
                {
                    ProcessMetadataDone(info.frameNum);
                }

                if (NULL != info.doneFence)
                {
                    CHIFENCECALLBACKINFO* pFenceCallbackInfo =
                        static_cast<CHIFENCECALLBACKINFO*>(CAMX_CALLOC(sizeof(CHIFENCECALLBACKINFO)));

                    if (NULL != pFenceCallbackInfo)
                    {
                        pFenceCallbackInfo->frameNum    = info.frameNum;
                        pFenceCallbackInfo->hChiSession = static_cast<CHIHANDLE>(this);
                        pFenceCallbackInfo->pUserData   = pPerRequestPorts;
                        pFenceCallbackInfo->size        = sizeof(CHIFENCECALLBACKINFO);
                        GetChiContext()->WaitChiFence(info.doneFence, ChiFenceCallback, pFenceCallbackInfo);
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupChi, "Unable to allocate structure, out of memory");
                        result = CamxResultENoMemory;
                    }
                }
                else
                {
                    // For Sink nodes signal that request is done.
                    if (TRUE == IsSinkNoBufferNode())
                    {
                        ProcessRequestIdDone(info.frameNum);
                    }

                    for (UINT i = 0; i < pPerRequestPorts->numOutputPorts; i++)
                    {
                        pOutputPort = &pPerRequestPorts->pOutputPorts[i];

                        if ((NULL != pOutputPort) && (NULL != pOutputPort->phFence))
                        {
                            CSLFenceSignal(*pOutputPort->phFence, CSLFenceResultSuccess);
                        }
                    }

                    for (UINT i = 0; i < pPerRequestPorts->numOutputPorts; i++)
                    {
                        if ((NULL != pOutputPort) && (TRUE == pOutputPort->flags.isDelayedBuffer))
                        {
                            if (TRUE == info.pBypassData[i].isBypassNeeded)
                            {
                                UINT inputIndex = info.pBypassData[i].selectedInputPortIndex;

                                // Select input port data
                                pOutputPort->pDelayedOutputBufferData->hFence =
                                    *pPerRequestPorts->pInputPorts[inputIndex].phFence;
                                pOutputPort->pDelayedOutputBufferData->pImageBuffer[0] =
                                    pPerRequestPorts->pInputPorts[inputIndex].pImageBuffer;
                                pOutputPort->pDelayedOutputBufferData->isParentInputBuffer = TRUE;
                            }
                            else
                            {
                                // Select output port data
                                if (NULL != pOutputPort->phFence)
                                {
                                    pOutputPort->pDelayedOutputBufferData->hFence = *pOutputPort->phFence;
                                }
                                else
                                {
                                    CAMX_LOG_ERROR(CamxLogGroupChi, "pOutputPort->phFence is NULL");
                                }
                                pOutputPort->pDelayedOutputBufferData->pImageBuffer[0] = pOutputPort->pImageBuffer[0];
                                pOutputPort->pDelayedOutputBufferData->isParentInputBuffer = FALSE;
                            }

                            CAMX_LOG_VERBOSE(CamxLogGroupChi, "Signaled Fence %08x(%08x) Image Buffer %x for request= %llu"
                                " parent buffer = %d input index = %d",
                                pOutputPort->phDelayedBufferFence,
                                *pOutputPort->phDelayedBufferFence,
                                pOutputPort->pDelayedOutputBufferData->pImageBuffer[0],
                                pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId,
                                pOutputPort->pDelayedOutputBufferData->isParentInputBuffer,
                                info.pBypassData[i].selectedInputPortIndex);

                            CSLFenceSignal(*pOutputPort->phDelayedBufferFence, CSLFenceResultSuccess);
                        }
                    }
                }
            }
        }
    }
    // CSL signal fence of outputport
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ChiNodeWrapper
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ChiNodeWrapper::ChiNodeWrapper()
{
    m_pNodeName = "ChiNodeWrapper";
    m_derivedNodeHandlesMetaDone = TRUE;
    m_canNodeSetBufferDependency = 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::~ChiNodeWrapper
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ChiNodeWrapper::~ChiNodeWrapper()
{
    if (NULL != m_phInputBuffer)
    {
        ReleaseChiBufferHandlePool(m_phInputBuffer);
        m_phInputBuffer = NULL;
        m_numInputPort  = 0;
    }

    if (NULL != m_phOutputBuffer)
    {
        ReleaseChiBufferHandlePool(m_phOutputBuffer);
        m_phOutputBuffer = NULL;
        m_numOutputPort  = 0;
    }

    if (NULL != m_pBypassData)
    {
        CAMX_FREE(m_pBypassData);
        m_pBypassData = NULL;
    }

    if (NULL != m_hNodeSession)
    {
        CHINODEDESTROYINFO info;
        info.size           = sizeof(info);
        info.hNodeSession   = m_hNodeSession;
        m_nodeCallbacks.pDestroy(&info);

        m_hNodeSession = NULL;
    }

    if (NULL != m_pComponentName)
    {
        CAMX_FREE(m_pComponentName);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::AllocateChiBufferHandlePool
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CHINODEBUFFERHANDLE* ChiNodeWrapper::AllocateChiBufferHandlePool(
    INT32 size)
{
    ChiImageList*        pImageList      = static_cast<ChiImageList*>(CAMX_CALLOC(sizeof(ChiImageList) * size));
    CHINODEBUFFERHANDLE* phBufferHandle  = static_cast<CHINODEBUFFERHANDLE*>(CAMX_CALLOC(sizeof(CHINODEBUFFERHANDLE) * size));

    if (NULL != pImageList && NULL != phBufferHandle)
    {
        for (INT32 i = 0; i < size; i++)
        {
            phBufferHandle[i]   = &pImageList[i];
        }
    }
    else
    {
        // one of the memory allocation is failed
        if (NULL != pImageList)
        {
            CAMX_FREE(pImageList);
            pImageList = NULL;
        }

        if (NULL != phBufferHandle)
        {
            CAMX_FREE(phBufferHandle);
            phBufferHandle = NULL;
        }
    }

    return phBufferHandle;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ReleaseChiBufferHandlePool
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID ChiNodeWrapper::ReleaseChiBufferHandlePool(
    CHINODEBUFFERHANDLE* phBufferHandle)
{
    if (NULL != phBufferHandle)
    {
        ChiImageList* pImageList = static_cast<ChiImageList*>(phBufferHandle[0]);
        if (NULL != pImageList)
        {
            CAMX_FREE(pImageList);
            pImageList = NULL;
        }
        CAMX_FREE(phBufferHandle);
        phBufferHandle = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ImageBufferToChiBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CHINODEBUFFERHANDLE ChiNodeWrapper::ImageBufferToChiBuffer(
    ImageBuffer*         pImageBuffer,
    CHINODEBUFFERHANDLE* phBufferHandle,
    INT32                index)
{
    CAMX_ASSERT(pImageBuffer != NULL);
    CAMX_ASSERT(phBufferHandle != NULL);

    CHINODEBUFFERHANDLE hHandle = phBufferHandle[index];
    const ImageFormat * pFormat = pImageBuffer->GetFormat();

    // @todo (CAMX-1806) check if Chi could use the Camx::ImageFormat directly
    // packing mismatch !!!! Utils::Memcpy(&hHandle->format, pFormat, sizeof(ImageFormat));
    if (NULL != pFormat)
    {
        hHandle->format.alignment      = pFormat->alignment;
        hHandle->format.colorSpace     = static_cast<ChiColorSpace>(pFormat->colorSpace);
        hHandle->format.height         = pFormat->height;
        hHandle->format.planeAlignment->scanlineAlignment = pFormat->planeAlignment->scanlineAlignment;
        hHandle->format.planeAlignment->strideAlignment   = pFormat->planeAlignment->strideAlignment;
        hHandle->format.rotation       = static_cast<ChiRotation>(pFormat->rotation);
        hHandle->format.width          = pFormat->width;
        hHandle->format.format         = static_cast<ChiFormat>(pFormat->format);
        for (UINT32 index = 0; index < ChiNodeFormatsMaxPlanes; index++)
        {
            hHandle->format.formatParams.yuvFormat[index].width         = pFormat->formatParams.yuvFormat[index].width;
            hHandle->format.formatParams.yuvFormat[index].height        = pFormat->formatParams.yuvFormat[index].height;
            hHandle->format.formatParams.yuvFormat[index].planeStride   = pFormat->formatParams.yuvFormat[index].planeStride;
            hHandle->format.formatParams.yuvFormat[index].sliceHeight   = pFormat->formatParams.yuvFormat[index].sliceHeight;
            hHandle->format.formatParams.yuvFormat[index].metadataStride= pFormat->formatParams.yuvFormat[index].metadataStride;
            hHandle->format.formatParams.yuvFormat[index].metadataHeight= pFormat->formatParams.yuvFormat[index].metadataHeight;
            hHandle->format.formatParams.yuvFormat[index].metadataSize  = pFormat->formatParams.yuvFormat[index].metadataSize;
            hHandle->format.formatParams.yuvFormat[index].pixelPlaneSize= pFormat->formatParams.yuvFormat[index].pixelPlaneSize;
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "NULL Image format pointer");
    }
    hHandle->imageCount = pImageBuffer->GetNumFramesInBatch();

    UINT numberOfPlanes     = pImageBuffer->GetNumberOfPlanes();
    hHandle->numberOfPlanes = numberOfPlanes;

    CAMX_ASSERT(numberOfPlanes <= ChiNodeFormatsMaxPlanes);
    for (UINT i = 0; i < numberOfPlanes; i++)
    {
        CSLMemHandle     hMemHandle     = 0;
        SIZE_T           offset         = 0;
        SIZE_T           metadataSize   = 0;

        pImageBuffer->GetPlaneCSLMemHandle(0, i, &hMemHandle, &offset, &metadataSize);
        hHandle->metadataSize[i]    = metadataSize;
        hHandle->planeSize[i]       = pImageBuffer->GetPlaneSize(i);
        hHandle->handles[i]         = hMemHandle;
    }

    for (UINT i = 0; i < hHandle->imageCount; i++)
    {
        ChiImage* pImage = &hHandle->pImageList[i];

        pImage->size            = sizeof(ChiImage);
        pImage->pNativeHandle   = pImageBuffer->GetNativeBufferHandle();
        for (UINT j = 0; j < numberOfPlanes; j++)
        {
            pImage->fd[j]       = pImageBuffer->GetFileDescriptor();
            pImage->pAddr[j]    = pImageBuffer->GetPlaneVirtualAddr(i, j);
        }
    }

    return hHandle;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::CDKResultToCamxResult
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAMX_INLINE CamxResult ChiNodeWrapper::CDKResultToCamxResult(
    CDKResult cdkResult)
{
    CamxResult result = CamxResultSuccess;
    if (CDKResultSuccess != cdkResult)
    {
        result = CamxResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::CamxResultToCDKResult
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAMX_INLINE CDKResult ChiNodeWrapper::CamxResultToCDKResult(
    CamxResult camxResult)
{
    CDKResult result = CDKResultSuccess;
    if (CamxResultSuccess != camxResult)
    {
        result = CDKResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ChiFenceCallback
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID ChiNodeWrapper::ChiFenceCallback(
    CHIFENCEHANDLE  hChiFence,
    VOID*           pUserData)
{
    CAMX_UNREFERENCED_PARAM(hChiFence);

    PerRequestOutputPortInfo* pOutputPort      = NULL;
    CHIFENCECALLBACKINFO*     pCallbackInfo    = reinterpret_cast<CHIFENCECALLBACKINFO*>(pUserData);

    CAMX_ASSERT(NULL != pCallbackInfo);
    CAMX_ASSERT(sizeof(CHIFENCECALLBACKINFO) == (pCallbackInfo->size));

    PerRequestActivePorts*    pPerRequestPorts = reinterpret_cast<PerRequestActivePorts*>(pCallbackInfo->pUserData);
    ChiNodeWrapper*           pNode            = static_cast<ChiNodeWrapper*>(pCallbackInfo->hChiSession);

    pNode->GetChiContext()->ReleaseChiFence(hChiFence);
    hChiFence = NULL;

    CAMX_ASSERT(NULL != pPerRequestPorts);

    // For Sink nodes signal that request is done.
    if (TRUE == pNode->IsSinkNoBufferNode())
    {
        pNode->ProcessRequestIdDone(pCallbackInfo->frameNum);
    }

    for (UINT i = 0; i < pPerRequestPorts->numOutputPorts; i++)
    {
        pOutputPort = &pPerRequestPorts->pOutputPorts[i];
        if ((NULL != pOutputPort) && (NULL != pOutputPort->phFence))
        {
            CSLFenceSignal(*pOutputPort->phFence, CSLFenceResultSuccess);
        }
    }

    CAMX_FREE(pCallbackInfo);
    pCallbackInfo = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::ChiFenceDependencyCallback
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID ChiNodeWrapper::ChiFenceDependencyCallback(
    CHIFENCEHANDLE  hChiFence,
    VOID*           pUserData)
{
    CHIFENCECALLBACKINFO* pCallbackInfo = reinterpret_cast<CHIFENCECALLBACKINFO*>(pUserData);

    CAMX_ASSERT(NULL != pCallbackInfo);
    CAMX_ASSERT(sizeof(CHIFENCECALLBACKINFO) == (pCallbackInfo->size));

    ChiContext* pContext = static_cast<ChiContext*>(pCallbackInfo->pUserData);

    pContext->ReleaseChiFence(hChiFence);
    hChiFence = NULL;

    CAMX_FREE(pCallbackInfo);
    pCallbackInfo = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::SetupNCSLink
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSSensor* ChiNodeWrapper::SetupNCSLink(
    NCSSensorConfig* pSensorConfig)
{
    CamxResult      result            = CamxResultEFailed;
    NCSService*     pNCSServiceObject = NULL;
    NCSSensorConfig sensorConfig      = { 0 };
    NCSSensorCaps   sensorCapsGyro    = {};
    NCSSensor*      pSensorObject     = NULL;

    CAMX_LOG_VERBOSE(CamxLogGroupChi, "Setting up an NCS link");
    if (NULL != pSensorConfig)
    {
        // Get the NCS service object handle
        pNCSServiceObject = reinterpret_cast<NCSService *>(HwEnvironment::GetInstance()->GetNCSObject());
        if (NULL != pNCSServiceObject)
        {
            result = pNCSServiceObject->QueryCapabilites(&sensorCapsGyro, pSensorConfig->sensorType);
            if (result == CamxResultSuccess)
            {
                // Clients responsibility to set it to that config which is supported
                sensorConfig.samplingRate  = pSensorConfig->samplingRate;   // in Hertz
                sensorConfig.reportRate    = pSensorConfig->reportRate;     // in micorsecs
                sensorConfig.operationMode = pSensorConfig->operationMode;  // Batched reporting mode
                sensorConfig.sensorType    = pSensorConfig->sensorType;
                pSensorObject = pNCSServiceObject->RegisterService(NCSGyroType, &sensorConfig);
                if (NULL == pSensorObject)
                {
                    CAMX_LOG_ERROR(CamxLogGroupChi, "Unable to register with the NCS !!");
                    result = CamxResultEFailed;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "Unable to Query caps error %s", CamxResultStrings[result]);
            }
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "NULL ncs input config pointer");
        result = CamxResultEInvalidPointer;
    }

    return pSensorObject;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ChiNodeWrapper::DestroyNCSLink
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::DestroyNCSLink(
    VOID* pSensorObj)
{
    CamxResult      result            = CamxResultEFailed;
    NCSSensor*      pSensorObject     = NULL;
    NCSService*     pNCSServiceObject = NULL;

    CAMX_LOG_VERBOSE(CamxLogGroupChi, "Setting up an NCS link");
    if (NULL != pSensorObj)
    {
        pSensorObject = static_cast<NCSSensor*>(pSensorObj);

        // Get the NCS service object handle
        pNCSServiceObject = reinterpret_cast<NCSService *>(HwEnvironment::GetInstance()->GetNCSObject());
        if (NULL != pNCSServiceObject)
        {
            result = pNCSServiceObject->UnregisterService(pSensorObject);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupChi, "Unregister %s", CamxResultStrings[result]);
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupChi, "Invalid service object %s", CamxResultStrings[result]);
        }

    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupChi, "NULL Sensor object handle");
        result = CamxResultEInvalidArg;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ChiNodeWrapper::OnStreamOn
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::OnStreamOn()
{
    CamxResult result = CamxResultSuccess;

    CAMX_LOG_VERBOSE(CamxLogGroupChi, "On stream on for node %d, instance %d", Name(), InstanceID());

    if (NULL != m_nodeCallbacks.pOnStreamOn)
    {
        CHINODEONSTREAMONINFO info = { 0 };
        info.size                  = sizeof(info);
        info.hNodeSession          = m_hNodeSession;

        result = CDKResultToCamxResult(m_nodeCallbacks.pOnStreamOn(&info));
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ChiNodeWrapper::OnStreamOff
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ChiNodeWrapper::OnStreamOff()
{
    CamxResult result = CamxResultSuccess;

    CAMX_LOG_VERBOSE(CamxLogGroupChi, "On stream off for node %d, instance %d", Name(), InstanceID());

    if (NULL != m_nodeCallbacks.pOnStreamOff)
    {
        CHINODEONSTREAMOFFINFO info = { 0 };
        info.size                   = sizeof(info);
        info.hNodeSession           = m_hNodeSession;

        result = CDKResultToCamxResult(m_nodeCallbacks.pOnStreamOff(&info));
    }

    return result;
}

CAMX_NAMESPACE_END
